package controller;

import java.net.InetAddress;

import javax.swing.DefaultListModel;

import java.net.UnknownHostException;
import java.util.concurrent.LinkedBlockingQueue;

import ni.*;
import gui.*;
import model.*;
import chatsystemTDa2.*;

/**
 * This class is the controller for the system
 */
public class ChatController {

	/**
	 * links to its components: chatNI, chatGUI, model
	 */
	private ChatNI chatNI;
	private ChatGUI chatGUI;
	private Model model;
	
	/**
	 * Categories of messages
	 */
	enum msg {
		Goodbye, Hello, HelloAck, Send, SendAck, FileRequest, FileResponse;
	}
	
	/**
	 * creates all its parts
	 * it passes the link of the parts of the model to the components
	 */
	public ChatController(){
		DefaultListModel userList = new DefaultListModel();
		LinkedBlockingQueue <Msg> listMsg2Snd = new LinkedBlockingQueue <Msg> ();
		LinkedBlockingQueue <Msg> listMsgRcvd = new LinkedBlockingQueue <Msg> ();
		this.model = new Model(this, "DefaultUsername", listMsg2Snd, userList, listMsgRcvd);
		this.chatNI = new ChatNI(this, listMsg2Snd);
		this.chatGUI = new ChatGUI(this, userList, listMsgRcvd);
	}
	
	/**
	 * Processes Hello message when the local user connects
	 * we create the message, as ip the broadcast address
	 * we add it in the model queue of messages to send
	 */
	public void process_hello(){
		try {
			Msg m = new Msg (new Hello(model.getUser().getUsername()), InetAddress.getByName("255.255.255.255"));
			model.addMsg2Snd(m);
		} catch (UnknownHostException e) {e.printStackTrace();}
	}

	/**
	 * processes the sending of a message Send by adding it in the model
	 * we don't handle the sendACK
	 * @param text is the text of the message to send
	 * @param u is the user recipient of the message to send
	 */
	public void process_send(String text, RemoteUser u){
		Msg m = new Msg (new Send(text, 1), u.getIP());
		model.addMsg2Snd(m);
	}
	
	/**
	 * processes the sending of a message Goodbye by adding it in the model
	 * when the user disconnects
	 * sending the message to every connected users
	 */
	public void process_goodbye(){
		for (int i = 0;i<this.chatGUI.getUserList().getSize();i++){
			Msg m = new Msg (new Goodbye(model.getUser().getUsername()), ((RemoteUser) this.chatGUI.getUserList().getElementAt(i)).getIP());
			model.addMsg2Snd(m);
		}	
	}

	/**
	 * processes the connection of the user: creates the local user, sends hellos and starts the chatNI
	 * @param username is the username that the local user chose
	 */
	public void process_connect(String username){
		System.out.println("Connecting...");
		model.setUsername(username);
		process_hello();
		System.out.println("Connected.");
		this.chatNI.start();
	}
	
	/**
	 * processes the disconnection of the local user: sends goodbyes
	 * closes threads from the ni and the gui and reinitialize the system
	 */
	public void process_disconnect(){
		process_goodbye();
		chatNI.close();
		chatGUI.close();
		initialize();
	}
	
	/**
	 * initialize the model, chatNI, chatGUI
	 */
	public void initialize(){
		DefaultListModel userList = new DefaultListModel();
		LinkedBlockingQueue <Msg> listMsg2Snd = new LinkedBlockingQueue <Msg> ();
		LinkedBlockingQueue <Msg> listMsgRcvd = new LinkedBlockingQueue <Msg> ();
		this.model = new Model(this, "DefaultUsername", listMsg2Snd, userList, listMsgRcvd);
		this.chatNI = new ChatNI(this, listMsg2Snd);
		this.chatGUI = new ChatGUI(this, userList, listMsgRcvd);
	}
	
	/**
	 * process the reception of messages according to the kind of message received
	 * modifies model and extract message: here: real treatment
	 * @param m is the message to process
	 */
	public void process_receive (Msg m) {
		msg typeMsg = msg.valueOf(m.getMsg().getClass().getSimpleName());
		try {
			if (!(m.getIp().equals(InetAddress.getLocalHost()))) {
				System.out.println("received : "+m.getMsg());
				switch(typeMsg){
				case Goodbye:
					chatGUI.closeConversation(m.getIp());
					model.delUser(m.getIp());
					break;
				case Hello:
					model.addUser(((Hello)m.getMsg()).getNickname(), m.getIp());
					Msg helloAck = new Msg(new HelloAck(model.getUser().getUsername()), m.getIp());
					model.addMsg2Snd(helloAck);
					break;
				case HelloAck:
					model.addUser(((HelloAck)m.getMsg()).getNickname(), m.getIp());
					break;
				case Send:
					model.addMsgRcvd(m);
					Msg sendAck = new Msg (new SendAck(((Send)m.getMsg()).getID()), m.getIp());
					model.addMsg2Snd(sendAck);
					break;
				case SendAck:
					break;
				case FileRequest:
					break;
				case FileResponse:
					break;
				default:
					//problem! exception...
					break;		
				}
			}
		} catch (UnknownHostException e) { e.printStackTrace(); }
	}
	
	
	/**
	 * tests
	 * 
	public static void main(String[] args) {
		ChatController ctrl = new ChatController();
		try {
			ctrl.model.addUser("Marc", InetAddress.getByName("150.5.55.2"));
			ctrl.model.addUser("Ju", InetAddress.getByName("255.255.255.255"));
			try {Thread.sleep(5000);
			} catch (InterruptedException e) {e.printStackTrace();}
			ctrl.model.addMsgRcvd(new Msg(new Send ("hello", 1), InetAddress.getByName("150.5.55.2")));
		} catch (UnknownHostException e1) {	e1.printStackTrace();}
	}*/
}
